#include <stdio.h>
#include "winsock.h"
#include "inetapi.h"

#pragma warning(disable: 4706)

class CNetAPI : public INetAPI
{
public:
	virtual void NetAdrToSockAddr(netadr_t *a, struct sockaddr *s);
	virtual void SockAddrToNetAdr(struct sockaddr *s, netadr_t *a);
	virtual char *AdrToString(netadr_t *a);
	virtual bool StringToAdr(const char *s, netadr_t *a);
	virtual void GetSocketAddress(int socket, netadr_t *a);
	virtual bool CompareAdr(netadr_t *a, netadr_t *b);
	virtual void GetLocalIP(netadr_t *a);
};

static CNetAPI g_NetAPI;
INetAPI *net = (INetAPI *)&g_NetAPI;

void CNetAPI::NetAdrToSockAddr(netadr_t *a, struct sockaddr *s)
{
	memset(s, 0, sizeof(*s));

	if (a->type == NA_BROADCAST)
	{
		((struct sockaddr_in *)s)->sin_family = AF_INET;
		((struct sockaddr_in *)s)->sin_port = a->port;
		((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
	}
	else if (a->type == NA_IP)
	{
		((struct sockaddr_in *)s)->sin_family = AF_INET;
		((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip;
		((struct sockaddr_in *)s)->sin_port = a->port;
	}
}

void CNetAPI::SockAddrToNetAdr(struct sockaddr *s, netadr_t *a)
{
	if (s->sa_family == AF_INET)
	{
		a->type = NA_IP;
		*(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;
		a->port = ((struct sockaddr_in *)s)->sin_port;
	}
}

char *CNetAPI::AdrToString(netadr_t *a)
{
	static char s[64];

	memset(s, 0, 64);

	if (a)
	{
		if (a->type == NA_LOOPBACK)
			sprintf(s, "loopback");
		else if (a->type == NA_IP)
			sprintf(s, "%i.%i.%i.%i:%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3], ntohs(a->port));
	}

	return s;
}

static bool StringToSockaddr(const char *s, struct sockaddr *sadr)
{
	struct hostent *h;
	char *colon;
	char copy[128];
	struct sockaddr_in *p;

	memset(sadr, 0, sizeof(*sadr));

	p = (struct sockaddr_in *)sadr;
	p->sin_family = AF_INET;
	p->sin_port = 0;

	strcpy(copy, s);

	for (colon = copy; *colon; colon++)
	{
		if (*colon == ':')
		{
			*colon = 0;
			p->sin_port = htons((short)atoi(colon + 1));
		}
	}

	if (copy[0] >= '0' && copy[0] <= '9' && strstr(copy, "."))
	{
		*(int *)&p->sin_addr = inet_addr(copy);
	}
	else
	{
		if (!(h = gethostbyname(copy)))
			return false;

		*(int *)&p->sin_addr = *(int *)h->h_addr_list[0];
	}

	return true;
}

bool CNetAPI::StringToAdr(const char *s, netadr_t *a)
{
	struct sockaddr sadr;

	if (!strcmp(s, "localhost"))
	{
		memset(a, 0, sizeof(*a));
		a->type = NA_LOOPBACK;
		return true;
	}

	if (!StringToSockaddr(s, &sadr))
		return false;

	SockAddrToNetAdr(&sadr, a);
	return true;
}

void CNetAPI::GetSocketAddress(int socket, netadr_t *a)
{
	char buff[512];
	struct sockaddr_in address;
	int namelen;

	memset(a, 0, sizeof(*a));

	gethostname(buff, 512);
	buff[512 - 1] = 0;

	StringToAdr(buff, a);

	namelen = sizeof(address);

	if (getsockname(socket, (struct sockaddr *)&address, (int *)&namelen) == 0)
		a->port = address.sin_port;
}

bool CNetAPI::CompareAdr(netadr_t *a, netadr_t *b)
{
	if (a->type != b->type)
		return false;

	if (a->type == NA_LOOPBACK)
		return true;

	if (a->type == NA_IP && a->ip[0] == b->ip[0] && a->ip[1] == b->ip[1] && a->ip[2] == b->ip[2] && a->ip[3] == b->ip[3] && a->port == b->port)
		return true;

	return false;
}

void CNetAPI::GetLocalIP(netadr_t *a)
{
	char s[64];

	if (!::gethostname(s,64))
	{
		struct hostent *localip = ::gethostbyname(s);

		if (localip)
		{
			a->type = NA_IP;
			a->port = 0;
			memcpy(a->ip, localip->h_addr_list[0], 4);
		}
	}
}